

//#include <string.h>			// memcpy
#include "debug.h"
#include "LPC23xx.h"
#include "usbapi.h"
#include "VCom.h"

//#include "utils.h"
#include "string.h"
#include "fifoBuf.h"
//#include "printf.h"

//#include "Application.h"
//#include "std_io.h"
//#include "ModuleDef.h"



int(*SendToHost)(void*, int);

#define BAUD_RATE	115200

#define INT_IN_EP			0x81
#define BULK_OUT_EP		0x05
#define BULK_IN_EP		0x82

#define MAX_PACKET_SIZE	64

#define LE_WORD(x)		((x)&0xFF),((x)>>8)

// CDC definitions
#define CS_INTERFACE				0x24
#define CS_ENDPOINT				0x25

#define	SET_LINE_CODING			0x20
#define	GET_LINE_CODING			0x21
#define	SET_CONTROL_LINE_STATE	0x22

#define	INT_VECT_NUM	0

#define IRQ_MASK 0x00000080

// data structure for GET_LINE_CODING / SET_LINE_CODING class requests
typedef struct {
	unsigned long		dwDTERate;
	unsigned char		bCharFormat;
	unsigned char		bParityType;
	unsigned char		bDataBits;
} TLineCoding;

static TLineCoding LineCoding = {115200, 0, 0, 8};
static unsigned char abBulkBuf[64];
static unsigned char abClassReqData[8];
static volatile int fBulkInBusy;
static volatile int fChainDone;

static unsigned char Tx_Data[VCOM_FIFO_SIZE];
static unsigned char Rx_Data[VCOM_FIFO_SIZE];

static FIFO_INF Fifo_Tx_Data;
static FIFO_INF Fifo_Rx_Data;

/* int VCOM_Event; */
/* volatile int RxThreshold, RxThresholdTrigger; */

/* int Rx_Threshold_Set ( int Threshold ) */
/* { */
/* 	Rx_Threshold = Threshold; */
/* } */

/* int Rx_Threshold_Trigger ( void ) */
/* { */
/* 	return RxThresholdTrigger; */
/* } */


static const unsigned char abDescriptors[] = {

// device descriptor
	0x12,
	DESC_DEVICE,
	LE_WORD(0x0101),			// bcdUSB
	0x02,						// bDeviceClass
	0x00,						// bDeviceSubClass
	0x00,						// bDeviceProtocol
	MAX_PACKET_SIZE0,			// bMaxPacketSize
	LE_WORD(0xFFFF),			// idVendor
	LE_WORD(0x0005),			// idProduct
	LE_WORD(0x0100),			// bcdDevice
	0x01,						// iManufacturer
	0x02,						// iProduct
	0x03,						// iSerialNumber
	0x01,						// bNumConfigurations

// configuration descriptor
	0x09,
	DESC_CONFIGURATION,
	LE_WORD(67),				// wTotalLength
	0x02,						// bNumInterfaces
	0x01,						// bConfigurationValue
	0x00,						// iConfiguration
	0xC0,						// bmAttributes
	0x32,						// bMaxPower
// control class interface
	0x09,
	DESC_INTERFACE,
	0x00,						// bInterfaceNumber
	0x00,						// bAlternateSetting
	0x01,						// bNumEndPoints
	0x02,						// bInterfaceClass
	0x02,						// bInterfaceSubClass
	0x01,						// bInterfaceProtocol, linux requires value of 1 for the cdc_acm module
	0x00,						// iInterface
// header functional descriptor
	0x05,
	CS_INTERFACE,
	0x00,
	LE_WORD(0x0110),
// call management functional descriptor
	0x05,
	CS_INTERFACE,
	0x01,
	0x01,						// bmCapabilities = device handles call management
	0x01,						// bDataInterface
// ACM functional descriptor
	0x04,
	CS_INTERFACE,
	0x02,
	0x02,						// bmCapabilities
// union functional descriptor
	0x05,
	CS_INTERFACE,
	0x06,
	0x00,						// bMasterInterface
	0x01,						// bSlaveInterface0
// notification EP
	0x07,
	DESC_ENDPOINT,
	INT_IN_EP,					// bEndpointAddress
	0x03,						// bmAttributes = intr
	LE_WORD(8),					// wMaxPacketSize
	0x0A,						// bInterval
// data class interface descriptor
	0x09,
	DESC_INTERFACE,
	0x01,						// bInterfaceNumber
	0x00,						// bAlternateSetting
	0x02,						// bNumEndPoints
	0x0A,						// bInterfaceClass = data
	0x00,						// bInterfaceSubClass
	0x00,						// bInterfaceProtocol
	0x00,						// iInterface
// data EP OUT
	0x07,
	DESC_ENDPOINT,
	BULK_OUT_EP,				// bEndpointAddress
	0x02,						// bmAttributes = bulk
	LE_WORD(MAX_PACKET_SIZE),	// wMaxPacketSize
	0x00,						// bInterval
// data EP in
	0x07,
	DESC_ENDPOINT,
	BULK_IN_EP,					// bEndpointAddress
	0x02,						// bmAttributes = bulk
	LE_WORD(MAX_PACKET_SIZE),	// wMaxPacketSize
	0x00,						// bInterval
	
	// string descriptors
	0x04,
	DESC_STRING,
	LE_WORD(0x0409),

	0x0E,
	DESC_STRING,
	'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0,

	0x14,
	DESC_STRING,
	'U', 0, 'S', 0, 'B', 0, 'S', 0, 'e', 0, 'r', 0, 'i', 0, 'a', 0, 'l', 0,

	0x12,
	DESC_STRING,
	'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0,

// terminating zero
	0
}; 


/**
	Local function to handle incoming bulk data
		
	@param [in] bEP
	@param [in] bEPStatus
 */
static void BulkOut(unsigned char bEP, unsigned char bEPStatus)
{
	//int i, iLen;
	int iLen;

	if (Fifo_AvailableToWrite(&Fifo_Rx_Data) < MAX_PACKET_SIZE) 
	{
		// may not fit into fifo
		return;
	}

	// get data from USB into intermediate buffer
	iLen = USBHwEPRead(bEP, abBulkBuf, sizeof(abBulkBuf));

	
	iLen = Fifo_Write(&Fifo_Rx_Data, abBulkBuf, iLen);
	
//	DBG("\rRx: %d", iLen);
}


/**
	Sends the next packet in chain of packets to the host
		
	@param [in] bEP
	@param [in] bEPStatus
 */
static void SendNextBulkIn(unsigned char bEP, int fFirstPacket)
{
	int iLen;

	// this transfer is done
	fBulkInBusy = FALSE;
	
	// first packet?
	if (fFirstPacket) 
	{
		fChainDone = FALSE;
	}

	// last packet?
	if (fChainDone) 
	{
		return;
	}
	
	// get up to MAX_PACKET_SIZE bytes from transmit FIFO into intermediate buffer
/* 	for (iLen = 0; iLen < MAX_PACKET_SIZE; iLen++) { */
/* 		if (!fifo_get(&txfifo, &abBulkBuf[iLen])) { */
/* 			break; */
/* 		} */
/* 	} */
	
	iLen = Fifo_nRead(&Fifo_Tx_Data, abBulkBuf, MAX_PACKET_SIZE); 
	
	// send over USB
	USBHwEPWrite(bEP, abBulkBuf, iLen);
	fBulkInBusy = TRUE;

	// was this a short packet?
	if (iLen < MAX_PACKET_SIZE) 
	{
		fChainDone = TRUE;
	}
}

/**
	Local function to handle outgoing bulk data
		
	@param [in] bEP
	@param [in] bEPStatus
 */
static void BulkIn(unsigned char bEP, unsigned char bEPStatus)
{
	SendNextBulkIn(bEP, FALSE);
}


/**
	Local function to handle the USB-CDC class requests
		
	@param [in] pSetup
	@param [out] piLen
	@param [out] ppbData
 */
static int HandleClassRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
{
	switch (pSetup->bRequest) {

		// set line coding
		case SET_LINE_CODING:
			DBG("SET_LINE_CODING\n");
			MemCpy((unsigned char *)&LineCoding, *ppbData, 7);
			//MemCpy(&LineCoding, *ppbData, 7);
			*piLen = 7;
			DBG("dwDTERate=%u, bCharFormat=%u, bParityType=%u, bDataBits=%u\n",
				 (unsigned int)LineCoding.dwDTERate,
				 LineCoding.bCharFormat,
				 LineCoding.bParityType,
				 LineCoding.bDataBits);
			break;

			// get line coding
		case GET_LINE_CODING:
//			DBG("GET_LINE_CODING\n");
			*ppbData = (unsigned char *)&LineCoding;
			*piLen = 7;
			break;

			// set control line state
		case SET_CONTROL_LINE_STATE:
			// bit0 = DTR, bit = RTS
//			DBG("SET_CONTROL_LINE_STATE %X\n", pSetup->wValue);
			break;

		default:
			return FALSE;
	}
	return TRUE;
}


/**
	Initialises the VCOM port.
	Call this function before using VCOM_putchar or VCOM_getchar
 */
static void VCOM_init(void)
{
	Fifo_Init(&Fifo_Rx_Data, Rx_Data, VCOM_FIFO_SIZE);  
	Fifo_Init(&Fifo_Tx_Data, Tx_Data, VCOM_FIFO_SIZE);  
	fBulkInBusy = FALSE;
	fChainDone = TRUE;
}

/* /\* return - 1 if OK or (-1) if fail *\/ */
/* static int VCOM_putchar(unsigned char Ch) */
/* { */
/* 	//return fifo_put(&txfifo, c) ? c : EOF; */
/* 	return  Fifo_Write(&Fifo_Tx_Data, (unsigned char*)&Ch, 1); */
/* } */

// return - bytes number or (-1) if fail
int VCOM_puts( char *s )
{
	if ( s == NULL )
	{
		return (-1);
	}
	return Fifo_Write( &Fifo_Tx_Data, (unsigned char*)s, (unsigned int)StrLen(s) );
}

//return - bytes number or (-1) if fail
int VCOM_put_data (char* DataBuffer, unsigned int DataSize)
{
	return  Fifo_Write(&Fifo_Tx_Data, (unsigned char*)DataBuffer, DataSize);
}

/* return - how many bytes was put into DataBuffer  */
int VCOM_get_data(unsigned char* DataBuffer)
{
	if ( DataBuffer == NULL )
	{
		return (-1);
	}

	return Fifo_Read(&Fifo_Rx_Data, DataBuffer);
}

/* return - how many bytes was put into DataBuffer  */
int VCOM_get_ndata(unsigned char* DataBuffer, int BytesNumber)
{
	return Fifo_nRead(&Fifo_Rx_Data, DataBuffer, BytesNumber);
}

/**
	Reads one character from VCOM port
	
	@returns character read, or EOF if character could not be read
 */
int VCOM_getchar(void)
{
	int c;
	
	//return fifo_get(&rxfifo, &c) ? c : EOF;
	return Fifo_nRead(&Fifo_Rx_Data, (unsigned char*)&c, 1) ? c : EOF;
}

void VCOM_Connect ( int Connect )
{
	USBHwConnect( Connect );
}


char HostCommand[HOST_COMMAND_SIZE];
unsigned int pHostCommand;

extern void Command_Processor( void );
extern void (*Execute_Module) (void);

int VCOM_COMMAND = 0;

void VCom_Monitor ( void )
{
//	static unsigned int VComWasEvent = 0;  
	
/* 	if ( VCOM_Event == VCOM_NEW_MESSAGE || VComWasEvent ) */
/* 	{ */
/* 		if ( Execute_Module == NULL ) */
/* 		{ */
/* 			VCOM_COMMAND = TRUE; */
/* 			Execute_Module = Command_Processor; */
/* 			VComWasEvent = FALSE; */
/* 		} */
/* 		else */
/* 		{ */
/* 			VComWasEvent = TRUE; */
/* 		} */
/* 	} */
}

char *VCom_Message;

void Vcom_Transmit ( void )
{
	if ( (fBulkInBusy == FALSE) && (fChainDone == TRUE) && (Fifo_AvailableToRead(&Fifo_Tx_Data) > 0) )
	{
		SendNextBulkIn(BULK_IN_EP, TRUE);
	}
}

void Drive_VCOM ( void )
{
	int Char;
	VCom_Message = NULL;
	
/* Receive */
	Char = VCOM_getchar(); 
	
	if ( Char != EOF  )
	{
		HostCommand[pHostCommand] = Char;

		if (HostCommand[pHostCommand] == '\r' )
		{
			HostCommand[pHostCommand] = 0;
			pHostCommand = 0;
			//DBG("\rDEV_VCOM: %s", HostCommand);
			//Cmd_Add_Command(HostCommand, DEV_VCOM);
			VCom_Message = HostCommand;
		}
		else
			if ( ++pHostCommand >= HOST_COMMAND_SIZE )
			{
				pHostCommand = 0;
			} 
	}

		/* Transmit */
	Vcom_Transmit();
}

extern unsigned char SysState, PrevSysState; 
#define   STOP_MACHINE	21


/* int VCOM_printf( const char *format, ... ) */
/* { */
/* 	int pc; */

/* 	char s[VCOM_FIFO_SIZE]; */

/* 	char *ps = s; */

/* 	va_list args; */
      
/* 	va_start( args, format ); */
	
/* 	if ( (pc = print( &ps, format, args)) > VCOM_FIFO_SIZE ) */
/* 	{ */
/* 		DBG("\n VCOM_printf: ERROR\r"); */
/* 		SysState = STOP_MACHINE; */
/* 	} */
/* 	else */
/* 	{ */
/* 		VCOM_puts(s); */
/* 	} */

/* 	return pc; */
/* } */

/* int Send_To_Host( void* Buffer, int BufferSize ) */
/* { */
/* 	int Result; */

/* 	if ( BufferSize == 0 ) */
/* 	{ */
/* 		BufferSize = StrLen(Buffer); */
/* 	} */

/* 	if ( BufferSize == (-1) || BufferSize == 0 ) */
/* 	{ */
/* 		return (-1); */
/* 	} */

/* 	Result = Fifo_Write( &Fifo_Tx_Data, (unsigned char*)Buffer, BufferSize ); */
	
/* 	if ( Result != (-1) ) */
/* 	{ */
/* 		SendNextBulkIn(BULK_IN_EP, TRUE); */
/* 	} */

/* 	return Result; */
/* } */ 


/**
	Interrupt handler
	
	Simply calls the USB ISR, then signals end of interrupt to VIC
 */
/* static void USBIntHandler(void) */
/* { */
/* 	USBHwISR(); */
/* 	VICVectAddr = 0x00;    // dummy write to VIC to signal end of ISR 	 */
/* } */

/**
	USB frame interrupt handler
	
	Called every milisecond by the hardware driver.
	
	This function is responsible for sending the first of a chain of packets
	to the host. A chain is always terminated by a short packet, either a
	packet shorter than the maximum packet size or a zero-length packet
	(as required by the windows usbser.sys driver).

 */

/* static void USBFrameHandler(unsigned short int wFrame) */
/* { */
/* 	//if (!fBulkInBusy && (fifo_avail(&txfifo) != 0)) */
/* 	if (!fBulkInBusy && (Fifo_AvailableToRead(&Fifo_Tx_Data) != 0)) */
/* 	{ */
/* //		DEBUG_LED_ON(FP_LED); */
/* 		// send first packet */
/* 		SendNextBulkIn(BULK_IN_EP, TRUE); */

/* //		DEBUG_LED_OFF(FP_LED); */
/* 	} */
/* } */


/**
	USB device status handler
	
	Resets state machine when a USB reset is received.
 */
static void USBDevIntHandler(unsigned char bDevStatus)
{
	if ((bDevStatus & DEV_STATUS_RESET) != 0) 
	{
		fBulkInBusy = FALSE;
	}
}
 

/*************************************************************************
	main
	====
**************************************************************************/
void USB_Start(void)
{
//	int i;
	// initialise stack
	USBInit();

	// register descriptors
	USBRegisterDescriptors(abDescriptors);

	// register class request handler
	USBRegisterRequestHandler(REQTYPE_TYPE_CLASS, HandleClassRequest, abClassReqData);

	// register endpoint handlers
	USBHwRegisterEPIntHandler(INT_IN_EP, NULL);
	USBHwRegisterEPIntHandler(BULK_IN_EP, BulkIn);
	USBHwRegisterEPIntHandler(BULK_OUT_EP, BulkOut);
	
	// register frame handler
	//USBHwRegisterFrameHandler(USBFrameHandler);	// Dima
	//USBHwRegisterFrameHandler(NULL);
	
	// register device event handler
	USBHwRegisterDevIntHandler(USBDevIntHandler);		// Dima
	
	// initialise VCOM
	
	//DBG("Starting USB communication\n");
	VICVectCntl22 = 0x01;
  //VICVectAddr22 = (unsigned int)USBIntHandler;
	VICVectAddr22 = (unsigned int)USBHwISR;
  
	// set up USB interrupt
	VICIntSelect &= ~(1UL<<22);               // select IRQ for USB
	VICIntEnable |= (1UL<<22);
	
	VCOM_init();

	// connect to bus
	USBHwConnect(TRUE);
	
	VCOM_puts("0");
	
//	SendNextBulkIn(BULK_IN_EP, TRUE);
	
	

/* 	for ( i = 0; i < 10; i++ ) */
/* 	{ */
/* 		SendNextBulkIn(BULK_IN_EP, TRUE); */
/* 	} */

	// echo any character received (do USB stuff in interrupt)
/* 	while (1)  */
/* 	{ */
/* 		c = VCOM_getchar(); */

/* 		if (c != EOF)  */
/* 		{ */
/* 			if ( c == '\r' ) */
/* 			{ */
/* 				USB_Rx_Buff[pUSB_Rx_Buff] = 0; */
/* 				pUSB_Rx_Buff = 0; */
/* 				VCOM_puts(USB_Rx_Buff); */
/* 				DBG("\rRec: %d", *USB_Rx_Buff); */
/* 				//SendNextBulkIn(BULK_IN_EP, 0); */
/* 				SendNextBulkIn(BULK_IN_EP, TRUE); */
/* 			} */
/* 			else */
/* 			{ */
/* 				USB_Rx_Buff[pUSB_Rx_Buff++] = (unsigned char)c; */
/* 			} */
/* 		}  */
/*  		//FramePolling();  */
/* 	} */
}

